package edu.gatech.spacetraderteam3.model;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import edu.gatech.spacetraderteam3.view.Assets;


/**
 * Class for Planets in the game
 * 
 * @author TBW
 * @version 1.0
 */
public class SpacePlanet implements Serializable {
    /**
     * The serial version UID for this planet used in saving
     */
    private static final long serialVersionUID = 6655539867240209898L;

    /**
     * a random number constant
     */
    private static final Random RAND = new Random();

    /**
     * The planet's name
     */
    private final String name;

    /**
     * The planet's image
     */
    private final Assets.Surface planetImage;

    /**
     * The planet' background tile
     */
    private final Assets.Tile planetTile;

    /**
     * The planet's rotating texture
     */
    private final Assets.PlanetTexture planetTexture;

    /**
     * The planet's government style
     */
    private final Government government;

    /**
     * The planet's technology level
     */
    private final TechLevel techLevel;

    /**
     * The planet's orbital radius around the sun
     */
    private final double orbitalRadius;

    /**
     * The planet's orbital speed around the sun
     */
    private final double orbitalSpeed;

    /**
     * The planet's trading resources
     */
    private final Inventory inv;

    /**
     * The planet's solar system
     */
    private SpaceSolarSystem solarSystem;

    /**
     * Constructor for prefabricated planets from the SSPrefab class
     * 
     * @param prefab
     *            The Prefab planets
     */
    public SpacePlanet(SPPrefab prefab) {
        name = prefab.planetName();
        planetImage = prefab.planetImage();
        planetTile = prefab.planetTile();
        planetTexture = prefab.planetTexture();
        orbitalRadius = prefab.orbitalRadius();
        orbitalSpeed = prefab.orbitalSpeed();
        government = prefab.government();
        techLevel = prefab.techLevel();
        inv = generateInventory();

    }

    /**
     * Constructor for Planets
     * 
     * @param name
     *            Name of the planet
     * @param government
     *            Government type of the planet
     * @param techLevel
     *            Techlevel of the planet
     * @param planetImage
     *            Image to use to display the planet
     * @param planetTile
     *            Background tile of the planet shown when docked
     * @param planetTexture
     *            Texture of the planet shown when docked
     * @param orbitalRadius
     *            Orbiting radius of the planet
     * @param orbitalSpeed
     *            Orbiting speed of the planet
     */
    public SpacePlanet(String name, Government government, TechLevel techLevel,
            Assets.Surface planetImage, Assets.Tile planetTile,
            Assets.PlanetTexture planetTexture, double orbitalRadius, double orbitalSpeed) {
        this.name = name;
        this.planetImage = planetImage;
        this.planetTile = planetTile;
        this.planetTexture = planetTexture;
        this.orbitalRadius = orbitalRadius;
        this.orbitalSpeed = orbitalSpeed;
        this.government = government;
        this.techLevel = techLevel;
        this.inv = generateInventory();
    }

    /**
     * Removes a resource from the planet's inventory
     * 
     * @param resource
     *            Resource to remove
     * @param num
     *            Number of the resource to remove
     * @return Whether the resource was removed
     */
    public boolean removeResource(Resource resource, int num) {
        return inv.removeResource(resource, num);
    }

    /**
     * Adds a resource to the Planet's inventory
     * 
     * @param resource
     *            Resource to add
     * @param num
     *            Number of resource to add
     * @return Whether the resource was added
     */
    public boolean addResource(Resource resource, int num) {
        return inv.addResource(resource, num);
    }

    /**
     * Gets the solar system the planet it is
     * 
     * @return The solar system
     */
    public SpaceSolarSystem getSystem() {
        return solarSystem;
    }

    /**
     * Sets the solar system the planet is contained in
     * 
     * @param solarSystem
     *            The solar system
     */
    public void setSystem(SpaceSolarSystem solarSystem) {
        this.solarSystem = solarSystem;
    }

    /**
     * Generates a random inventory
     * 
     * @return The random inventory
     */
    private Inventory generateInventory() {
        final Inventory inv = new Inventory();

        // for testing - will replace this with actual thing to calculate
        for (Resource res : Resource.values()) {
            if (res.minTech() - this.techLevel.level() <= 0) {
                inv.addResource(res, 999);
            }
        }
        return inv;
    }

    /**
     * Checks if the planet can sell a resource
     * 
     * @param resource
     *            The resource to check
     * @return Whether the planet can sell the resource
     */
    private boolean canSellTo(Resource resource) {
        return techLevel.level() >= resource.minTech();
    }

    /**
     * Get's the state of the planet
     * 
     * @return the solar system's state
     */
    public State getState() {
        return this.solarSystem.getState();
    }

    /**
     * Gets the price of a resource
     * 
     * @param resource
     *            Resource to check
     * @return The price
     */
    public int getResourcePrice(Resource resource) {

        // calculate using basePrice + (priceIncreasePerTechLevel * (TechLevel -
        // MinTechLevelPermitted) +/- variance
        int price = resource.basePrice()
                + (resource.ipl() * (this.techLevel.level() - resource.minTech()));
        price += RAND.nextBoolean() ? RAND.nextInt(resource.variance()) : -1
                * RAND.nextInt(resource.variance());
        return resource.modify(this.solarSystem.getState(), this.government, price);
    }

    /**
     * Gets a hashmap of the buy prices
     * 
     * @return The hashmap of buy prices
     */
    public Map<Resource, Integer> getPrices() {
        final Map<Resource, Integer> prices = new HashMap<Resource, Integer>();
        for (Resource res : Resource.values()) {
            if (inv.getResource(res) > 0) {
                prices.put(res, getResourcePrice(res));
            }
        }
        return prices;
    }

    /**
     * Gets a hasmap for the sell prices
     * 
     * @return The hashmap of the sell prices.
     */
    public Map<Resource, Integer> getSellPrices() {
        final Map<Resource, Integer> prices = new HashMap<Resource, Integer>();

        for (Resource res : Resource.values()) {
            if (canSellTo(res)) {
                prices.put(res, getResourcePrice(res));
            }
        }

        return prices;
    }

    /**
     * Getter for the name of the Planet
     * 
     * @return The name of the planet
     */
    public String getName() {
        return name;
    }

    /**
     * Getter for the planet texture
     * 
     * @return The planet texture
     */
    public Assets.PlanetTexture getTexture() {
        return planetTexture;
    }

    /**
     * Getter for the orbiting radius of the planet
     * 
     * @return The radius
     */
    public double getOrbitalRadius() {
        return orbitalRadius;
    }

    /**
     * Getter for the orbiting speed
     * 
     * @return The orbital speed
     */
    public double getOrbitalSpeed() {
        return orbitalSpeed;
    }
    
    public TechLevel getTechLevel() {
    	return techLevel;
    }
    
    public Government getGovernment() {
    	return government;
    }

    /**
     * Method that returns string of information about the planet
     * 
     * @return The string.
     */
    public String toString() {
        return name + ", with " + government + " government, " + techLevel
                + " tech level, orbital radius " + orbitalRadius + ", orbital speed "
                + orbitalSpeed;
    }

    /**
     * Creates and returns the paint delegate for the planet
     * 
     * @return The paint delegate
     */
    public PlanetPaintDelegate getPlanetPaintDelegate() {
        return new PlanetPaintDelegate() {
            public Assets.Tile getTile() {
                return planetTile;
            }

            public Assets.Surface getSurface() {
                return planetImage;
            }
        };
    }

    /**
     * Paint delegate interface
     * 
     * @author TBw
     * 
     */
    public interface PlanetPaintDelegate {
        /**
         * @return the tile
         */
        Assets.Tile getTile();

        /**
         * @return the surface
         */
        Assets.Surface getSurface();
    }
}
